﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.IO.IsolatedStorage;
using CSKLiteWindowsPhone.Common;
using SQLiteClient;
using CSKLiteWindowsPhone.WebsiteData;
using System.IO;
using System.Text.RegularExpressions;
using Coding4Fun.Phone.Controls;
using System.Threading;
using Wintellect.Threading.AsyncProgModel;
using Microsoft.Phone.Tasks;
using System.Text;
using Microsoft.Phone.Shell;

namespace CSKLiteWindowsPhone
{
    public partial class RunTest : PhoneApplicationPage
    {
        private object locker = new object();
        private string _token = string.Empty;
        private string _userName = string.Empty;

        SQLiteConnection _db = null;
        WebsiteDataSoapClient websiteDataSoapClient;
        TestResult _currentTestResult = new TestResult();
        string _sessionId = string.Empty;
        IEnumerable<TestCase> testCases = null;
        IEnumerable<TestProcedure> testProcs = null;
        IEnumerable<TestCase> targetTestCases = null;
        List<TestSessionResult> _currentSessionResult = new List<TestSessionResult>();
        private AsyncEnumerator m_ae;
        string _accessUrlErrorMessage = string.Empty;
        public RunTest()
        {
            InitializeComponent();
            this._token = IsolatedStorageSettings.ApplicationSettings[Constants.LoginTockenKey] as string;
            this._userName = IsolatedStorageSettings.ApplicationSettings[Constants.LoginUserNameKey] as string;

            this.Loaded += new RoutedEventHandler(RunTest_Loaded);
        }

        void RunTest_Loaded(object sender, RoutedEventArgs e)
        {
            this._db = Utility.GetConnection();
            this.GetTestData();
            var category = (from c in this.testCases
                            group c by c.Category into tc
                            select tc.Key);
            
            if (category.Count() > 0)
            {
                this.listCategory.ItemsSource = category;
                this.listCategory.SetValue(ListPicker.ItemCountThresholdProperty, 1);
                this.listCategory.ExpansionMode = ExpansionMode.ExpansionAllowed;
                this.listCategory.SelectionMode = SelectionMode.Multiple;
            }


            SQLiteCommand cmd = this._db.CreateCommand("SELECT * FROM Proxy");
            IEnumerable<Proxy> tests = cmd.ExecuteQuery<Proxy>();

            if (tests.Count() > 0)
            {
                this.listProxy.SetValue(ListPicker.ItemCountThresholdProperty, 1);
                this.listCategory.ExpansionMode = ExpansionMode.ExpansionAllowed;
                this.listProxy.ItemsSource = tests.Select(p => p.Name);
            }
        }
        private void btnRunTest_Click(object sender, RoutedEventArgs e)
        {

            this.ApplicationBar.IsVisible = false;
            progressOverlay.Show();

            IsolatedStorageSettings.ApplicationSettings["CurrentSessionResult"] = null;
            IsolatedStorageSettings.ApplicationSettings.Remove("CurrentSessionResult");
            this._sessionId = Guid.NewGuid().ToString();
            this.GetTestData();
            string source = string.Empty;
            targetTestCases = testCases.Where(tc => tc.Type == "WS" && tc.InputType == "URL"
                                                    && (listCategory.SelectedItems == null || listCategory.SelectedItems.Contains("ALL") ||
                                                    listCategory.SelectedItems.Contains(tc.Category))).OrderBy(t => t.Id);

            m_ae = new AsyncEnumerator();
            m_ae.BeginExecute(DownloadImages(m_ae), m_ae.EndExecute);
        }

        private IEnumerator<int> DownloadImages(AsyncEnumerator ae)
        {
            try
            {
                ae.ThrowOnMissingDiscardGroup(true);
                HttpWebRequest[] requests = this.targetTestCases.Select(t =>
                {
                    HttpWebRequest request = WebRequest.Create(new Uri(t.Input)) as HttpWebRequest;
                    request.Method = "GET";
                    return request;
                }).ToArray();

                for (int requestNum = 0; requestNum < requests.Length; requestNum++)
                {
                    requests[requestNum].BeginGetResponse(
                       ae.EndVoid(0, asyncResult =>
                       {
                           requests[requestNum].EndGetResponse(asyncResult).Close();
                       }), requestNum);
                }

                for (int resultNum = 0; resultNum < requests.Length; resultNum++)
                {
                    yield return 1;
                    if (ae.IsCanceled()) break;
                    IAsyncResult asyncResult = ae.DequeueAsyncResult();
                    int index = (int)asyncResult.AsyncState;
                    HttpStatusCode httpStatusCode = 0;
                    string reason = string.Empty;
                    string code = "Error";
                    TestProcedure foundedTestProcedure = null;
                    string sequence = string.Empty;
                    string source = string.Empty;
                    var procs = testProcs.Where(tp => tp.Code == "BP" && tp.ParameterType == "IFRAME").OrderBy(tp => tp.Sequence);
                    var item = this.targetTestCases.ToArray()[resultNum];
                    try
                    {
                        using (WebResponse response = requests[index].EndGetResponse(asyncResult))
                        {
                            using (Stream responseStream = response.GetResponseStream())
                            {
                                using (var sr = new StreamReader(responseStream))
                                {
                                    source = sr.ReadToEnd();
                                    var found = false;
                                    foreach (var iframe in procs)
                                    {
                                        Regex regex = new Regex(iframe.Parameter.Replace("{IFRAME}", "(.*)"));
                                        Match match = regex.Match(source);
                                        if (match.Success && match.Groups.Count >= 2)
                                        {
                                            found = true;
                                            string url = match.Groups[1].Value;
                                            WebRequest request = WebRequest.Create(new Uri(url)) as HttpWebRequest;
                                            request.Method = "GET";
                                            request.BeginGetResponse(result =>
                                            {
                                                try
                                                {
                                                    HttpWebResponse res = request.EndGetResponse(result) as HttpWebResponse;
                                                    httpStatusCode = res.StatusCode;
                                                    source = new StreamReader(res.GetResponseStream()).ReadToEnd();
                                                    procs = testProcs.Where(tp => tp.Code == "BP" && tp.ParameterType == "REASON").OrderBy(tp => tp.Sequence);
                                                    foreach (var reasonProc in procs)
                                                    {
                                                        regex = new Regex(reasonProc.Parameter.Replace("{REASON}", "([A-Za-z0-9\\s]*)"));
                                                        match = regex.Match(source);
                                                        if (match.Success && match.Groups.Count >= 2)
                                                        {
                                                            reason = match.Groups[1].Value;
                                                            foundedTestProcedure = reasonProc;
                                                            if (foundedTestProcedure != null)
                                                            {
                                                                code = foundedTestProcedure.Code;
                                                            }
                                                            else if (httpStatusCode == 0 || httpStatusCode == HttpStatusCode.NotFound || (int)httpStatusCode >= 500 && (int)httpStatusCode < 599)
                                                            {
                                                                code = "Error";
                                                                sequence = reasonProc.Sequence;
                                                            }
                                                            else
                                                            {
                                                                //TODO:
                                                                code = "Retrieved";
                                                                sequence = reasonProc.Sequence;
                                                            }
                                                            break;
                                                        }
                                                    }

                                                    if (foundedTestProcedure == null)
                                                    {
                                                        code = "Retrieved";
                                                    }
                                                }
                                                catch (Exception)
                                                {
                                                    code = "Error";
                                                    sequence = iframe.Sequence;
                                                }
                                                try
                                                {
                                                    Deployment.Current.Dispatcher.BeginInvoke(() =>
                                                    {
                                                        this.StoreTestResult(item, httpStatusCode, reason, code, foundedTestProcedure);
                                                    });
                                                }
                                                catch (Exception ex)
                                                {
                                                    string s = ex.Message;
                                                }
                                            }, null);

                                            break;
                                        }

                                        if (!found)
                                        {
                                            code = "Retrieved";
                                            this.StoreTestResult(this.targetTestCases.ToArray()[resultNum], httpStatusCode, reason, code, foundedTestProcedure);
                                        }
                                    }
                                }

                                this.UpdateTestStatus(resultNum);

                            }
                        }
                    }
                    catch (WebException ex)
                    {
                        this._accessUrlErrorMessage = string.Format("When access to Url:{0} error happened. \n{1}", this.targetTestCases.ToArray()[resultNum].Input,
                            ex.Message);
                        this.StoreTestResult(this.targetTestCases.ToArray()[resultNum], httpStatusCode, reason, code, foundedTestProcedure);
                        this.UpdateTestStatus(resultNum);
                    }
                }
            }
            finally
            {
                ToastPrompt toast = new ToastPrompt();
                string error = this._accessUrlErrorMessage;
                toast.TextWrapping = TextWrapping.Wrap;
                toast.Message = error;
            }
        }

        private void UpdateTestStatus(int testedCount)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                this.txtDB.Text = string.Format("{0} / {1}", testedCount + 1, targetTestCases.Count());
                if (testedCount + 1 == targetTestCases.Count())
                {
                    IsolatedStorageSettings.ApplicationSettings["CurrentSessionResult"] = this._currentSessionResult;
                    this.ApplicationBar.IsVisible = true;
                    progressOverlay.Hide();

                    foreach (var item in this.ApplicationBar.Buttons)
	                {
                        (item as ApplicationBarIconButton).IsEnabled = true;
	                } 
                }
            });
        }

        protected override void OnRemovedFromJournal(System.Windows.Navigation.JournalEntryRemovedEventArgs e)
        {
            base.OnRemovedFromJournal(e);

            this._db.Dispose();
            this._db = null;
        }

        private void StoreTestResult(TestCase item, HttpStatusCode httpStatusCode, string reason, string code, TestProcedure foundedTestProcedure)
        {
            TestSessionResult result = new TestSessionResult();
            result.SessionId = this._sessionId;
            result.TestId = item.Id;
            result.Sequence = foundedTestProcedure != null ? foundedTestProcedure.Sequence : string.Empty;
            result.Code = code;
            result.Type = foundedTestProcedure != null ? foundedTestProcedure.ParameterType : string.Empty;
            result.TypeValue = reason;
            result.Type2 = "";
            result.Type2Value = httpStatusCode.ToString() ;
            this._currentSessionResult.Add(result);
        }

        private void GetTestData()
        {
            SQLiteCommand cmd = this._db.CreateCommand("SELECT * FROM TestCase");
            this.testCases = cmd.ExecuteQuery<TestCase>();


            cmd = this._db.CreateCommand("SELECT * FROM TestProcedure");
            this.testProcs = cmd.ExecuteQuery<TestProcedure>();

            string sql = string.Format(@"INSERT INTO TestSession(
                                            Id,
                                            Time,
                                            Association,
                                            AssociationValue,
                                            SessionResultUploaded
                                            ) VALUES('{0}', '{1}', '{2}', '{3}', '{4}')",
                                            this._sessionId,
                                            DateTime.UtcNow,
                                            "contactId",
                                            "contactValue",
                                            '0');


            cmd = this._db.CreateCommand(sql);
            cmd.ExecuteNonQuery();
        }

        private void btnUpload_Click(object sender, EventArgs e)
        {
            //string sql = string.Format("SELECT * FROM TestSession WHERE Id = '{0}'", "da2756d3-0e3f-4ff0-bfbf-e970d46ce637");

            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                progressOverlayUpload.Show();
            });
            string sql = string.Format("SELECT * FROM TestSession WHERE Id = '{0}' AND SessionResultUploaded = '0'", this._sessionId);
            SQLiteCommand cmd = this._db.CreateCommand(sql);
            
            TestSession testSession = cmd.ExecuteQuery<TestSession>().First();

            
            //sql = string.Format("SELECT * FROM TestSessionResult WHERE SessionId = '{0}'", "da2756d3-0e3f-4ff0-bfbf-e970d46ce637");
            //sql = string.Format("SELECT * FROM TestSessionResult WHERE SessionId = '{0}'", this._sessionId);
            //cmd = this._db.CreateCommand(sql);
            //TestSessionResult[] results = cmd.ExecuteQuery<TestSessionResult>().ToArray();
            var tempResults = IsolatedStorageSettings.ApplicationSettings["CurrentSessionResult"] as List<TestSessionResult>;
            TestSessionResult[] results = tempResults.ToArray();

            IList<DetailedResult> detailedResults = new List<DetailedResult>();

            Array.ForEach<TestSessionResult>(results, new Action<TestSessionResult>((result) =>
            {
                DetailedResult r = new DetailedResult();
                r.Code = result.Code;
                r.StepId = result.Sequence;
                r.TestId = result.TestId;
                List<ResultType> resultTypes = new List<ResultType>();
                if (!string.IsNullOrEmpty(result.Type) && !string.IsNullOrEmpty(result.TypeValue))
                {
                    resultTypes.Add(new ResultType() { Name = result.Type, Value = result.TypeValue });
                }
                if (!string.IsNullOrEmpty(result.Type2) && !string.IsNullOrEmpty(result.Type2Value))
                {
                    resultTypes.Add(new ResultType() { Name = result.Type2, Value = result.Type2Value });
                }
                r.ResultTypes = resultTypes.ToArray();
                detailedResults.Add(r);
            }));

            testSession.DetailedResults = detailedResults.ToArray();

            List<CategoryResult> categoryResults = new List<CategoryResult>();

            var a = from r in results
                    join tp in this.testCases on r.TestId equals tp.Id 
                    select new
                    {
                       Code = r.Code,
                       SessionId = r.SessionId,
                       Squence = r.Sequence,
                       TestId = r.TestId,
                       Type = r.Type,
                       TypeValue = r.TypeValue,
                       Type2 = r.Type2,
                       Type2Value = r.Type2Value,
                       Category = tp.Category
                    };

            var ac = from i in a
                     group i by i.Category into c
                     select new CategoryResult
                     {
                         Category = c.Key,
                         Results = (from r in a
                                    where r.Category == c.Key
                                    group r by r.Code into rg
                                    select new Result1{
                                        Code = rg.Key,
                                        Count = rg.Count().ToString()
                                    }).ToArray()
                     };


            testSession.CategoryResults = ac.ToArray();

            this._currentTestResult.Token = this._token;
            this._currentTestResult.Username = this._userName;
            this._currentTestResult.Sessions = new TestSession[]{testSession};

            try
            {
                this.websiteDataSoapClient = Utility.GetWebsiteDataSoapClient();
                this.websiteDataSoapClient.UploadTestResultsAsync(this._currentTestResult);
                this.websiteDataSoapClient.UploadTestResultsCompleted += new EventHandler<UploadTestResultsCompletedEventArgs>(websiteDataSoapClient_UploadTestResultsCompleted);
            }
            catch (Exception ex)
            {
                ToastPrompt tp = new ToastPrompt();
                tp.Message = ex.Message;

                tp.Show();
            }
        }

        void websiteDataSoapClient_UploadTestResultsCompleted(object sender, UploadTestResultsCompletedEventArgs e)
        {
            try
            {
                if (e.Result.Errors.Count() == 0)
                {
                    
                    this._db = Utility.GetConnection();
                    SQLiteCommand cmd = null;
                    var tempResults = IsolatedStorageSettings.ApplicationSettings["CurrentSessionResult"] as List<TestSessionResult>;
                    foreach (var item in tempResults)
                    {
                        cmd = this._db.CreateCommand(string.Format(@"INSERT INTO TestSessionResult(
                                                                                                            SessionId,
                                                                                                            TestId,
                                                                                                            Squence,
                                                                                                            Code,
                                                                                                            Type,
                                                                                                            TypeValue,
                                                                                                            Type2,
                                                                                                            Type2Value
                                                                                                            ) VALUES('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}')",
                                                                        item.SessionId,
                                                                        item.TestId,
                                                                        item.Sequence,
                                                                        item.Code,
                                                                        item.Type,
                                                                        item.TypeValue,
                                                                        item.Type2,
                                                                        item.Type2Value,
                                                                        "1"));

                        //try
                        //{
                            cmd.ExecuteNonQuery();
                        //}
                        //catch (Exception ex)
                        //{
                        //    string s = ex.Message;
                        //}
                    }

                    SQLiteCommand cmd2 = this._db.CreateCommand(string.Format("UPDATE TestSession SET SessionResultUploaded = '1' WHERE Id = '{0}'", this._sessionId));
                    cmd2.ExecuteNonQuery();

                    Deployment.Current.Dispatcher.BeginInvoke(() =>
                    {
                        (this.ApplicationBar.Buttons[0] as ApplicationBarIconButton).IsEnabled = false;
                    });
                    ToastPrompt toast = new ToastPrompt();
                    toast.Message = "Upload succeeded";
                    toast.Show();
                }
                else
                {
                    ToastPrompt toast = new ToastPrompt();
                    Array.ForEach(e.Result.Errors, new Action<Error>((err) => { toast.Message += err.Message; }));
                    toast.Show();
                }

            }
            catch (Exception ex)
            {
                ToastPrompt toast = new ToastPrompt();
                toast.TextWrapping = TextWrapping.Wrap;
                string error = ex.Message;
                toast.Message = error;
                toast.Show();
            }

            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                progressOverlayUpload.Hide();
            });
        }

        private void btnView_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri(string.Format("/TestResultReport.xaml?sessionid={0}", this._sessionId), UriKind.Relative));
        }

        private void btnEmail_Click(object sender, EventArgs e)
        {
                SQLiteCommand cmd = this._db.CreateCommand("SELECT * FROM TestCase");
                this.testCases = cmd.ExecuteQuery<TestCase>();

                string sql = string.Format("SELECT * FROM TestSession WHERE Id = '{0}'", this._sessionId);
                cmd = this._db.CreateCommand(sql);

                TestSession testSession = cmd.ExecuteQuery<TestSession>().First();
                var tempResults = IsolatedStorageSettings.ApplicationSettings["CurrentSessionResult"] as List<TestSessionResult>;
                TestSessionResult[] results = tempResults.ToArray();

                List<DetailedResult> detailedResults = new List<DetailedResult>();

                Array.ForEach<TestSessionResult>(results, new Action<TestSessionResult>((result) =>
                {
                    DetailedResult r = new DetailedResult();
                    r.Code = result.Code;
                    r.StepId = result.Sequence;
                    r.TestId = result.TestId;
                    List<ResultType> resultTypes = new List<ResultType>();
                    if (!string.IsNullOrEmpty(result.Type) && !string.IsNullOrEmpty(result.TypeValue))
                    {
                        resultTypes.Add(new ResultType() { Name = result.Type, Value = result.TypeValue });
                    }
                    if (!string.IsNullOrEmpty(result.Type2) && !string.IsNullOrEmpty(result.Type2Value))
                    {
                        resultTypes.Add(new ResultType() { Name = result.Type2, Value = result.Type2Value });
                    }
                    r.ResultTypes = resultTypes.ToArray();
                    detailedResults.Add(r);
                }));

                testSession.DetailedResults = detailedResults.ToArray();

                List<CategoryResult> categoryResults = new List<CategoryResult>();
                var a = from r in results
                        join tp in this.testCases on r.TestId equals tp.Id
                        select new
                        {
                            Code = r.Code,
                            SessionId = r.SessionId,
                            Sequence = r.Sequence,
                            TestId = r.TestId,
                            Type = r.Type,
                            TypeValue = r.TypeValue,
                            Type2 = r.Type2,
                            Type2Value = r.Type2Value,
                            Category = tp.Category
                        };

                var ac = from i in a
                         group i by i.Category into c
                         select new CategoryResult
                         {
                             Category = c.Key,
                             Results = (from r in a
                                        where r.Category == c.Key
                                        group r by r.Code into rg
                                        select new Result1
                                        {
                                            Code = rg.Key,
                                            Count = rg.Count().ToString()
                                        }).ToArray()
                         };


                testSession.CategoryResults = ac.ToArray();
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < testSession.CategoryResults.Count(); i ++ )
                {
                    int count = 0;
                    var item = testSession.CategoryResults[i];
                    foreach (var c in item.Results)
                    {
                        count += int.Parse(c.Count);
                    }

                    sb.Append(item.Category);
                    sb.Append(Environment.NewLine);
                    sb.Append(count);
                    sb.Append(Environment.NewLine);

                    foreach (var tile in item.Results)
                    {
                        sb.Append("           " + tile.Code);
                        sb.Append(Environment.NewLine);
                        sb.Append("           " + tile.Count.ToString());
                        sb.Append(Environment.NewLine);
                    }
                }

            new EmailComposeTask
            {
                Subject = string.Format("CSK Lite Test Result for Session: {0}", this._sessionId),
                Body = sb.ToString(),
                To = "bbai@websense.com"
            }.Show();
        }
    }
}